﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CivLib.CivNum
{
  /// <summary>
  /// Base class for equaions
  /// </summary>
  public abstract class EquationBase : ISoluableEqn
  {
    private string name;
    /// <summary>Name of the equation</summary>
    public string Name { get { return name; } }
    /// <summary>vector of the variables</summary>
    public VarVec vars;

    private EquationBase() { ;}

    /// <summary>
    /// Main constructor for the equation
    /// </summary>
    /// <param name="a_name">name of the equation</param>
    /// <param name="a_numVars">number of variables in the equation</param>
    /// <param name="a_varNames">array of the variable names</param>
    protected EquationBase(string a_name, int a_numVars, string[] a_varNames = null)
    {
      vars = new VarVec(a_numVars, a_varNames);
      name = a_name;
    }

    /// <summary>
    /// The current error in the function.  This is equivalent to putting all
    /// terms on one side of the equation and then evaluating what should be zero
    /// when this reaches zero the current varialbes are valid.
    /// </summary>
    /// <returns></returns>
    public abstract double Residual();

    /// <summary>
    /// indexer for accessing the variables
    /// </summary>
    /// <param name="i">index of the variable</param>
    /// <returns>the variable</returns>
    public double this[int i]
    {
      get { return vars[i]; }
      set { vars[i] = value; ;}
    }

    /// <summary>
    /// Gets the array of bools defining which variables are constrained
    /// </summary>
    public bool[] Constrained
    {
      get { return vars.Constrained; }
      set { vars.Constrained = value; }
    }

    /// <summary>
    /// Gets the array of variable names
    /// </summary>
    public string[] VarName
    {
      get { return vars.Names; }
      set { vars.Names = value; }
    }

    /// <summary>
    /// Gets the number of variables in the equation
    /// </summary>
    public int Count
    {
      get { return vars.Count; }
    }
  }


  /// <summary>
  /// This class is a list of variables.  This library is concerned with
  /// physical computation so assuming that variables would be doubles seemed
  /// like an ok assumption.  Signifacant performance benifits come from being
  /// strongly typed to avoid boxing and unboxing.
  /// </summary>
  public class VarVec
  {
    private int count;
    private string[] names;
    private bool[] constrained;
    private double[] values;

    #region Parameters
    /// <summary>
    /// Number of variables
    /// </summary>
    public int Count
    {
      get { return count; }
    }
    /// <summary>
    /// Array of the variable names
    /// </summary>
    public string[] Names
    {
      get { return names; }
      set { names = value; }
    }
    /// <summary>
    /// True if the index is constrained
    /// </summary>
    public bool[] Constrained
    {
      get { return constrained; }
      set { constrained = value; }
    }
    /// <summary>
    /// Gets value at index
    /// </summary>
    /// <param name="i">variable index</param>
    /// <returns>value at index</returns>
    public double this[int i]
    {
      get { return values[i]; }
      set { values[i] = value; }
    }
    #endregion

    #region Constructor
    /// <summary>
    /// Constructor for VarVec
    /// </summary>
    /// <param name="numberOfVariables">number of variables to contain</param>
    /// <param name="variableNames">names of the variables</param>
    public VarVec(int numberOfVariables, string[] variableNames = null)
    {
      count = numberOfVariables;
      names = new string[count];
      constrained = new bool[count];  //bool array is initialized to false
      values = new double[count];     //double array is initialized to 0.0D
      if (variableNames != null)
        Array.Copy(variableNames, names, variableNames.Length);
    }
    #endregion

    #region Methods
    /// <summary>
    /// Sets variable and makes it constrained
    /// </summary>
    /// <param name="i">index of constrained variable</param>
    /// <param name="value">value of constrained variable</param>
    public void Set(int i, double value)
    {
      values[i] = value;
      constrained[i] = true;
    }

    #endregion

  }

  /// <summary>
  /// Class to contain a variable for an equation
  /// </summary>
  /// <typeparam name="T">Value Type that variable will store</typeparam>
  class Variable<T> :
      IConvertible, IComparable, IComparable<T>, IEquatable<T>,
      IFormattable
  {
    #region Paramaters
    private string name;
    private bool constrained = false;
    private T val;

    /// <summary>Name of the variable</summary>
    public String N { get { return name; } set { name = value; } }

    /// <summary>If the variable is constrained or not</summary>
    public bool C { get { return constrained; } set { constrained = value; } }

    /// <summary>Value of the variable</summary>
    public T V { get { return val; } set { val = value; } }
    #endregion

    #region Constructors
    Variable() { ;}

    public Variable(string varName, T varValue)
    {
      name = varName;
      val = varValue;
    }
    #endregion

    #region IConvertable Interface
    TypeCode IConvertible.GetTypeCode()
    {
      return Convert.GetTypeCode(val);
    }

    bool IConvertible.ToBoolean(IFormatProvider provider)
    {
      return Convert.ToBoolean(val);
    }

    byte IConvertible.ToByte(IFormatProvider provider)
    {
      return Convert.ToByte(val);
    }

    char IConvertible.ToChar(IFormatProvider provider)
    {
      return Convert.ToChar(val);
    }

    DateTime IConvertible.ToDateTime(IFormatProvider provider)
    {
      return Convert.ToDateTime(val);
    }

    decimal IConvertible.ToDecimal(IFormatProvider provider)
    {
      return Convert.ToDecimal(val);
    }

    double IConvertible.ToDouble(IFormatProvider provider)
    {
      return Convert.ToDouble(val);
    }

    short IConvertible.ToInt16(IFormatProvider provider)
    {
      return Convert.ToInt16(val);
    }

    int IConvertible.ToInt32(IFormatProvider provider)
    {
      return Convert.ToInt32(val);
    }

    long IConvertible.ToInt64(IFormatProvider provider)
    {
      return Convert.ToInt64(val);
    }

    sbyte IConvertible.ToSByte(IFormatProvider provider)
    {
      return Convert.ToSByte(val);
    }

    float IConvertible.ToSingle(IFormatProvider provider)
    {
      return Convert.ToSingle(val);
    }
    object IConvertible.ToType(Type conversionType, IFormatProvider provider)
    {
      return Convert.ChangeType(val, conversionType);
    }

    ushort IConvertible.ToUInt16(IFormatProvider provider)
    {
      return Convert.ToUInt16(val);
    }

    uint IConvertible.ToUInt32(IFormatProvider provider)
    {
      return Convert.ToUInt32(val);
    }

    ulong IConvertible.ToUInt64(IFormatProvider provider)
    {
      return Convert.ToUInt64(val);
    }
    #endregion

    public int CompareTo(object obj)
    {
      throw new NotImplementedException();
    }

    public int CompareTo(T other)
    {
      throw new NotImplementedException();
    }

    #region ToString
    public override string ToString()
    {
      string valString = val.ToString();
      return this.GetType().ToString();
    }

    public string ToString(IFormatProvider provider)
    {
      return Convert.ToString(val, provider);
    }

    public string ToString(string format, IFormatProvider formatProvider)
    {
      throw new NotImplementedException();
    }
    #endregion

    public bool Equals(T other)
    {
      return val.Equals(other);
    }

  }
}
